﻿// (c) Norbert Huffschmid
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.

using System;
using log4net;
using NAudio.CoreAudioApi;
using NAudio.Wave;
using NProtocol;
using NProtocol.Codec;
using ShoutBox.Core;
using ShoutBox.Service.Properties;

namespace ShoutBox.Service
{
    /// <summary>
    /// Windows-based platform adapter for audio output
    /// </summary>
    public class WindowsWaveOutPlatformAdapter : IPlatformAdapterAudioOut
    {
        /// <summary>
        /// Gets the shoutbox name
        /// </summary>
        public string ShoutBoxName
        {
            get { return Environment.MachineName; }
        }


        private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private BufferedWaveProvider bufferedWaveProvider;
        private WaveOut waveOut;
        private bool volumeLevelSaved = false;
        private float origMasterVolumeLevel;
        private bool origMuteCondition;
        private bool disposed = false;


        /// <summary>
        /// Constructor
        /// </summary>
        public WindowsWaveOutPlatformAdapter()
        {
            log.Debug("Constructor");

            // init wave out
            WaveCallbackInfo callbackInfo = WaveCallbackInfo.FunctionCallback();
            this.bufferedWaveProvider = new BufferedWaveProvider(new WaveFormat(8000, 16, 1));
            this.bufferedWaveProvider.DiscardOnBufferOverflow = true;
            this.waveOut = new WaveOut(callbackInfo);
            this.waveOut.Init(this.bufferedWaveProvider);
            this.waveOut.DeviceNumber = -1;
            this.waveOut.Play();
        }


        /// <summary>
        /// Play PCM audio data
        /// </summary>
        /// <param name="pcmData">PCM audio data (8kHz-16bit-mono encoded)</param>
        public void PlayPcmData(byte[] pcmData)
        {
            log.Debug("PlayPcmData - pcmData: " + pcmData.Length.ToString() + " bytes.");

            this.bufferedWaveProvider.AddSamples(pcmData, 0, pcmData.Length);
        }

        /// <summary>
        /// Save current volume level for later restoring
        /// </summary>
        public void SaveVolumeLevel()
        {
            log.Debug("SaveVolumeLevel");

            if (volumeLevelSaved == false)
            {
                try
                {
                    MMDeviceEnumerator devEnum = new MMDeviceEnumerator();
                    MMDevice speakers = devEnum.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);

                    // save master volume settings
                    this.origMasterVolumeLevel = speakers.AudioEndpointVolume.MasterVolumeLevelScalar;
                    this.origMuteCondition = speakers.AudioEndpointVolume.Mute;

                    volumeLevelSaved = true;
#if DEBUG
                    if (true)
#else
                    if (Settings.Default.IgnoreSpeakerSettings == true)
#endif
                    {
                        // un-mute speakers
                        speakers.AudioEndpointVolume.Mute = false;

                        if (this.origMasterVolumeLevel < Settings.Default.ForcedVolumeLevel)
                        {
                            // increase master volume level to predefined value
                            speakers.AudioEndpointVolume.MasterVolumeLevelScalar = Settings.Default.ForcedVolumeLevel;
                        }
                    }

                }
                catch (NotSupportedException ex)
                {
                    log.Error(ex.Message, ex);
                }
            }
        }

        /// <summary>
        /// Restore previously saved volume level
        /// </summary>
        public void RestoreVolumeLevel()
        {
            log.Debug("RestoreVolumeLevel");

            if (volumeLevelSaved == true)
            {
                try
                {
                    MMDeviceEnumerator devEnum = new MMDeviceEnumerator();
                    MMDevice speakers = devEnum.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);

                    // restore application volume settings
                    speakers.AudioEndpointVolume.MasterVolumeLevelScalar = this.origMasterVolumeLevel;
                    speakers.AudioEndpointVolume.Mute = this.origMuteCondition;

                    volumeLevelSaved = false;
                }
                catch (NotSupportedException ex)
                {
                    log.Error(ex.Message, ex);
                }
            }
        }

        /// <summary>
        /// Set volume level
        /// </summary>
        /// <param name="volume">Volume level</param>
        public void SetVolumeLevel(double volume)
        {
            // not supported

            log.Debug("RestoreVolumeLevel");
        }

        /// <summary>
        /// Release all resources
        /// </summary>
        public void Dispose()
        {
            log.Debug("Dispose");

            Dispose(true);
            GC.SuppressFinalize(this);
        }


        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    this.waveOut.Dispose();
                }
            }
            disposed = true;
        }
    }
}
